﻿#define _CRT_SECURE_NO_WARNINGS 1

#include "CentralCache.h"
#include "PageCache.h"

CentralCache CentralCache::_sInst;

// 获取⼀个⾮空的span
Span* CentralCache::GetOneSpan(SpanList& list, size_t size)
{
	Span* it = list.Begin();
	//有就返回
	while (it != list.End())
	{
		if (it->_freelist != nullptr)
			return it;
		else
			it = it->_next;
	}

	//没有  问pagecache 要
	//解开桶锁  让其他线程能往回还
	//其他线程申请咋办？ 都没空闲的span了
	list._mtx.unlock();

	//计算这个桶对应的内存块大小要用多少页  得出要申请的页数
	size_t npage = SizeClass::NumPage(size);
	//上锁  开始操作
	PageCache::GetInstance()->_pageMtx.lock();
	Span* span = PageCache::GetInstance()->NewSpan(npage);//获取npage个页
	span->_objSize = size;//记录span切分的内存块大小
	span->isUse = true;   //是否使用标记为true
	PageCache::GetInstance()->_pageMtx.unlock();


	//计算span起始页地址 然后切分
	char* start = (char*)(span->_pageid << PAGE_SHIFT);
	size_t bytes = span->_n << PAGE_SHIFT;
	char* end = start + bytes;

	//尾插
	span->_freelist = start;
	start += size;
	void* tail = span->_freelist;

	while (start < end)
	{
		Next(tail) = start;
		tail = Next(tail);
		start += size;
	}
	// 尾节点next置空
	Next(tail) = nullptr;
	//给桶锁加回来
	list._mtx.lock();
	//把span插入桶
	list.PushFront(span);

	return span;
}

// 从中⼼缓存获取⼀定数量的对象给thread cache
size_t CentralCache::FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size)
{
	//1 找桶
	size_t bucketNum = SizeClass::Index(size);
	//上锁
	_spanlist[bucketNum]._mtx.lock();
	Span* span = GetOneSpan(_spanlist[bucketNum], size);

	assert(span);
	assert(span->_freelist);

	//2 找完桶并成功获取一个非空span
	//接下来就要拿一批了   不够的情况下有多少拿多少
	start = span->_freelist;
	end = start;
	size_t i = 0;
	size_t trueNum = 1;
	for (; i < batchNum-1&& Next(end) != nullptr; ++i)
	{
		end = Next(end);
		++trueNum;
	}
	span->_freelist = Next(end);
	Next(end) = nullptr;
	span->_useCount += trueNum;

	_spanlist[bucketNum]._mtx.unlock();
	return trueNum;
}

// 将⼀定数量的对象释放到span跨度
// thread还给我  我得把这些内存块放到span里 
// 哪个span？从哪里来到哪里去 建立映射来查找
// 为什么要 放回对应的span里  usecount 有序 好管理 也方便知道能不能还给page
void CentralCache::ReleaseListToSpans(void* start, size_t size)
{
	//哪个桶
	size_t index = SizeClass::Index(size);
	_spanlist[index]._mtx.lock();
	//找
	while (start)
	{
		void* next = Next(start);//用来迭代  因为start要参与插入 所以提前记录下一个位置
		Span* span = PageCache::GetInstance()->MapObjectToSpan(start);
		Next(start) = span->_freelist;
		span->_freelist = start;
		span->_useCount--;

		if (span->_useCount == 0)//span空闲了
		{
			_spanlist[index].Erase(span);
			span->_freelist = nullptr;//?    我记录了 pageid 和 页的数量  要还的内存区域通过这两个找 空闲链表这些指针置空
			span->_next = nullptr;
			span->_prev = nullptr;

			_spanlist[index]._mtx.unlock();//解开桶锁
			PageCache::GetInstance()->_pageMtx.lock();//上个大锁
			PageCache::GetInstance()->ReleaseSpanToPageCache(span);//还给pagecache
			PageCache::GetInstance()->_pageMtx.unlock();
			_spanlist[index]._mtx.lock();
		}
		start = next;
	}
	_spanlist[index]._mtx.unlock();
}